<?php

namespace App\Package\Attendance;

use App\Models\Schedule;
use App\Exceptions\GeneralException;
use App\Models\System\Infrastructure;

/**
 * 时间处理
 *
 * Date: 2018/6/1
 * @author George
 * @package App\Package\Attendance
 */

class Time
{
	/**
	 * 检查考勤状态
	 *
	 * Date: 2018/6/1
	 * @author George
	 * @param Schedule $schedule
	 * @param string $time
	 * @param string $type
	 * @param int $company_id
	 * @return \Illuminate\Config\Repository|mixed
	 */
	public static function check(Schedule $schedule, string $time, string $type, int $company_id)
	{
		$time = strtotime($time);
		$scope = Infrastructure::query()->where('company_id', $company_id)->first();
		// 获取公司的签到允许误差
		$delayed = $scope ? $scope->late * 60 : config('attendance.delayed') * 60;
		// 签到状态判断
		if ($type === 'checkin') {
			$reference = strtotime($schedule->start_timestamp);
			// 判断是否迟到
			if ($reference < ($time - $delayed)) {

				// 如果迟到，判断是否超出旷工时间范围
				if (($reference + $scope->absenteeism) < $time) {
					// 超出范围则判定为旷工
					return config('attendance.status.absenteeism');
				}
				// 判定为迟到

				return config('attendance.status.tardy');
			}

		}

		// 签退状态判断
		if ($type === 'checkout') {
			$reference = strtotime($schedule->end_timestamp);
			if ($reference > $time) {
				return config('attendance.status.early');
			}
		}

		// 判定为正常考勤
		return config('attendance.status.normal');
	}

	/**
	 * 时间节点生成器
	 *
	 * Date: 02/05/2018
	 * @author George
	 * @param int $step 步长
	 * @param string $start 开始时间
	 * @param string $end 结束时间
	 * @return array
	 */
	public static function nodes(int $step = 30, string $start = '00:00', string $end = '23:59')
	{
		$times = [];
		$start = strtotime(date("Y-m-d {$start}:00"));
		$end = strtotime(date("Y-m-d {$end}:59"));
		while ($start < $end) {
			$start = strtotime(date('Y-m-d H:i:s', $start) . "+ {$step}minute");
			$times[] = date('H:i', $start);
		}
		return $times;
	}

	/**
	 * Date: 2018/6/2
	 * @author George
	 * @deprecated
	 * @param string $start
	 * @param string $end
	 */
	public static function difference(string $start, string $end)
	{
		$start_time = strtotime(date("Y-m-d {$start}:00"));

		if ($start > $end) {
			$end_time = strtotime(date("Y-m-d {$end}:00") . '+1day');
		} else {
			$end_time = strtotime(date("Y-m-d {$end}:00"));
		}
	}

	/**
	 * 根据签到时间和签退时间
	 * 结算具体到某天的结束时间
	 *
	 * Date: 21/04/2018
	 * @author George
	 * @param $time_start
	 * @param $time_end
	 * @param string $date
	 * @return string
	 */
	public static function endTime($time_start, $time_end, string $date)
	{
		if ($time_end < $time_start) {
			return date('Y-m-d', strtotime('+1 day', strtotime($date))) . ' ' . $time_end;
		}
		return $date . ' ' . $time_end;
	}

	/**
	 * 计算工作时间
	 *
	 * Date: 05/05/2018
	 * @author George
	 * @param string $date
	 * @param string $start
	 * @param string $end
	 * @return false|int
	 */
	public static function worktime(string $date, string $start, string $end)
	{
		$fullStart = $date . ' ' . $start;
		$fullEnd = self::endTime($start, $end, $date);
		return strtotime($fullEnd) - strtotime($fullStart);
	}

	/**
	 * Date: 2018/6/2
	 * @author George
	 * @deprecated
	 * @param string $start
	 * @param string $end
	 * @param string $schdule_start
	 * @param string $schedule_end
	 */
	public static function actualHour(string $start, string $end, string $schdule_start, string $schedule_end)
	{

	}

	/**
	 * 验证时间节点
	 *
	 * Date: 18/05/2018
	 * @author George
	 * @param string $start
	 * @param string $end
	 * @param array $timings
	 * @param int $delayed
	 * @throws GeneralException
	 */
	public static function validateTimings(string $start, string $end, array $timings, int $delayed)
	{
		$startTime = strtotime($start);
		$endTime = strtotime($end);

		$previous = false;

		foreach ($timings as $index => $timing) {
			$timstamp = strtotime($timing['time']);
			self::timingScope($startTime, $endTime, $timing);
			self::timingInterval($previous, $timing, $delayed);
			$previous = $timstamp;
		}
	}

	/**
	 * 验证报岗时间点是否在班次范围内
	 *
	 * Date: 18/05/2018
	 * @author George
	 * @param $start
	 * @param $end
	 * @param array $timing
	 * @throws GeneralException
	 */
	public static function timingScope($start, $end, array $timing)
	{
		$timstamp = strtotime($timing['time']);
		if ($span = $start > $end) {
			if ($timstamp < $start && $timstamp > $end) {
				throw new GeneralException("时间{$timing['time']}在班次首尾时间之外", 400);
			}
		} else {
			if ($timstamp < $start || $timstamp > $end) {
				throw new GeneralException("时间{$timing['time']}在班次首尾时间之外", 400);
			}
		}
	}

	/**
	 * 验证时间间距是否有效
	 *
	 * Date: 18/05/2018
	 * @author George
	 * @param $previous "上一时间节点的时间戳"
	 * @param $next "当前时间节点的时间戳"
	 * @param int $delayed
	 * @throws GeneralException
	 */
	public static function timingInterval($previous, $next, int $delayed)
	{
		// 计算误差时间
		$delayed *= (60 * 2);
		$nextTime = strtotime($next['time']);

		// 判断两个时间点是否跨天
		if ($previous && $previous > ($nextTime + 43200)) {
			// 计算上一个时间点到00:00:00的秒数
			$before_midnight = 86400 - ($previous - strtotime('00:00:00'));
			// 计算从00:00:00到当前时间点的秒数
			$after_midnight = $nextTime - strtotime('00:00:00');

			// 判断两个时间之和是否小于允许误差时间
			if (($before_midnight + $after_midnight) < $delayed) {
				throw new GeneralException(date('H:i:s', $previous) . "和{$next['time']}的间隔或顺序有误", 400);
			}
		} else {
			// 非跨天情况下直接判断两者时间间隔是否大于误差时间
			if ($previous && $nextTime < ($previous + $delayed)) {
				throw new GeneralException(date('H:i:s', $previous) . "和{$next['time']}的间隔或顺序有误", 400);
			}
		}
	}
}
